#include "ossMem.h"
#include "ossErr.h"
#include "ossUtil.h"

#include "pd.hpp"

#include "msg.h"
#include "msgDef.h"

#include "_sdbapi.h"

qc::ConnectionContext::ConnectionContext()
    :send_buff(NULL),
    send_buff_size(0),
    endian_convert(0),
    recv_buff(NULL),
    recv_buff_size(0)
{
}

qc::ConnectionContext::~ConnectionContext()
{
    if (send_buff)
    {
        SDB_OSS_FREE(send_buff);
        send_buff = NULL;
    }
    if (recv_buff)
    {
        SDB_OSS_FREE(recv_buff);
        recv_buff = NULL;
    }
}

SINT32 qc::steal_size(const CHAR* buff)
{
    return *(SINT32*)buff;
}

INT32 qc::build_create_collection_space_command(
    qc::ConnectionContext* conn_ctx,
    const CHAR* cs_name,
    INT32 page_size)
{
    INT32 rc = SDB_OK;
    CHAR* cmd = CMD_ADMIN_PREFIX CMD_NAME_CREATE_COLLECTIONSPACE;
    if(ossStrlen(cs_name) > CLIENT_CS_NAMESZ)
    {
        rc = SDB_INVALIDARG;
        return rc;
    }
    
    bson::BSONObj new_obj = BSON(FIELD_NAME_NAME << cs_name \
        << FIELD_NAME_PAGE_SIZE << page_size);

    rc = qc::_build_command(conn_ctx, cmd, &new_obj, NULL, NULL, NULL);
    
done:
    return rc;
error:
    goto done;
}

INT32 qc::build_drop_collection_space_command(
    qc::ConnectionContext* conn_ctx,
    const CHAR* cs_name)
{
    INT32 rc = SDB_OK;
    CHAR* cmd = CMD_ADMIN_PREFIX CMD_NAME_DROP_COLLECTIONSPACE;
    if(ossStrlen(cs_name) > CLIENT_CS_NAMESZ)
    {
        rc = SDB_INVALIDARG;
        return rc;
    }
    
    bson::BSONObj new_obj = BSON(FIELD_NAME_NAME << cs_name);

    rc = qc::_build_command(conn_ctx, cmd, &new_obj, NULL, NULL, NULL);
    
done:
    return rc;
error:
    goto done;
}

INT32 qc::build_create_collection_command(
    qc::ConnectionContext* conn_ctx,
    const CHAR* cs_name,
    const CHAR* c_name,
    const CHAR* options
)
{
    INT32 rc = SDB_OK;
    CHAR* cmd = CMD_ADMIN_PREFIX CMD_NAME_CREATE_COLLECTION;
    bson::BSONObj new_obj;
    bson::BSONObj* bson_options = new bson::BSONObj(options);
    if(!cs_name \
        || !c_name \
        || ossStrlen(c_name) > CLIENT_COLLECTION_NAMESZ \
        || ossStrlen(c_name) == 0 \
        || ossStrlen(cs_name) == 0 \
        || !bson_options)
    {
        rc = SDB_INVALIDARG;
        return rc;
    }
    
    std::string c_fullname = std::string(cs_name) + "." + std::string(c_name);
    bson::BSONObjBuilder ob ;
    ob.append(FIELD_NAME_NAME, c_fullname);
    {
        bson::BSONObjIterator it ( *bson_options ) ;
        while(it.more())
        {
            ob.append(it.next());
        }
    }
    new_obj = ob.obj();
    rc = qc::_build_command(conn_ctx, cmd, &new_obj, NULL, NULL, NULL);

done:
    if(bson_options)
    {
        delete bson_options;
        bson_options = NULL;
    }
    return rc;

}

INT32 qc::build_drop_collection_command(
    qc::ConnectionContext* conn_ctx,
    const CHAR* cs_name,
    const CHAR* c_name
)
{
    INT32 rc = SDB_OK;
    CHAR* cmd = CMD_ADMIN_PREFIX CMD_NAME_DROP_COLLECTION;
    bson::BSONObj new_obj;
    if(!cs_name \
        || !c_name \
        || ossStrlen(c_name) > CLIENT_COLLECTION_NAMESZ \
        || ossStrlen(c_name) == 0 \
        || ossStrlen(cs_name) == 0
    )
    {
        rc = SDB_INVALIDARG;
        return rc;
    }
    
    std::string c_fullname = std::string(cs_name) + "." + std::string(c_name);
    bson::BSONObjBuilder ob ;
    ob.append(FIELD_NAME_NAME, c_fullname);
    new_obj = ob.obj();
    rc = qc::_build_command(conn_ctx, cmd, &new_obj, NULL, NULL, NULL);
done:
    return rc;
}

INT32 qc::build_insert_command(
    qc::ConnectionContext* conn_ctx,
    const CHAR* cs_name,
    const CHAR* c_name,
    const CHAR* bytes_record//,
//    bson::OID* id
)
{
    INT32 rc = SDB_OK;
    if(!cs_name \
        || !c_name \
        || ossStrlen(c_name) == 0 \
        || ossStrlen(cs_name) == 0 \
    )
    {
        rc = SDB_INVALIDARG;
        return rc;
    }
    std::string c_fullname = std::string(cs_name) + "." + std::string(c_name);

    bson::BSONObj tmp;
    bson::BSONObj* obj = new bson::BSONObj(bytes_record);
    if(!obj)
    {
        rc = SDB_OOM;
        goto error;
    }
    rc = _append_oid(*obj, tmp);
    if(rc)
    {
        goto error;
    }
    rc = clientBuildInsertMsgCpp(
        &(conn_ctx->send_buff),
        &(conn_ctx->send_buff_size),
        c_fullname.c_str(),
        0,
        0,
        tmp.objdata(),
        conn_ctx->endian_convert);
    if(rc)
    {
        goto error;
    }
    
done:
    if(obj)
    {
        delete obj;
        obj = NULL;
    }
    return rc;
error:
    goto done;
}

#pragma pack(1)
   class _IDToInsert
   {
   public :
      CHAR _type ;
      CHAR _id[4] ; // _id + '\0'
      bson::OID _oid ;
      _IDToInsert ()
      {
         _type = (CHAR)bson::jstOID ;
         _id[0] = '_' ;
         _id[1] = 'i' ;
         _id[2] = 'd' ;
         _id[3] = 0 ;
         SDB_ASSERT ( sizeof ( _IDToInsert) == 17,
                      "IDToInsert should be 17 bytes" ) ;
      }
   } ;
   typedef class _IDToInsert _IDToInsert ;
   class _idToInsert : public bson::BSONElement
   {
   public :
      _idToInsert( CHAR* x ) : BSONElement((CHAR*) ( x )){}
   } ;
   typedef class _idToInsert _idToInsert ;
#pragma pack()


INT32 qc::_append_oid(const bson::BSONObj& input, bson::BSONObj& output)
{
    INT32 rc = SDB_OK;
    INT32 size = 0;
    CHAR* buff = NULL;

    _IDToInsert oid;
    _idToInsert oidEle((CHAR*)(&oid)) ;

    // copy from clientcpp.cpp
      INT32 oidLen = 0 ;
      if ( !input.getField( CLIENT_RECORD_ID_FIELD ).eoo() )
      {
         output = input ;
         goto done ;
      }
      oid._oid.init() ;
      oidLen = oidEle.size() ;
      size = ossRoundUpToMultipleX ( input.objsize() +
                                           oidLen,
                                           SDB_PAGE_SIZE ) ;
      buff = (CHAR*)SDB_OSS_MALLOC(sizeof(CHAR)*size);
      if(!buff)
      {
        rc = SDB_OOM;
        goto error;
      }
      *(INT32*)(buff) = input.objsize() + oidLen ;
      ossMemcpy ( &buff[sizeof(INT32)], oidEle.rawdata(),
                  oidEle.size() ) ;
      ossMemcpy ( &buff[sizeof(INT32)+oidLen],
                  (CHAR*)(input.objdata()+sizeof(INT32)),
                  input.objsize()-sizeof(INT32) ) ;
      output.init (buff) ;
done:
    if(buff)
    {
        SDB_OSS_FREE(buff);
        buff = NULL;
    }
    return rc;
error:
    goto done;
}

INT32 qc::build_query_command(
    qc::ConnectionContext* conn_ctx,
    const CHAR* cs_name,
    const CHAR* c_name,
    const CHAR* bytes_condition,
    const CHAR* bytes_selector,
    const CHAR* bytes_order_by,
    const CHAR* bytes_hint,
    INT64 num_to_skip,
    INT64 num_to_return,
    INT32 flag
)
{
    INT32 rc = SDB_OK;
    std::string c_fullname = std::string(cs_name) + "." + std::string(c_name);
    
    rc = clientBuildQueryMsgCpp(
        &(conn_ctx->send_buff),
        &(conn_ctx->send_buff_size),
        c_fullname.c_str(),
        flag,
        0,
        num_to_skip,
        num_to_return,
        bytes_condition,
        bytes_selector,
        bytes_order_by,
        bytes_hint,
        conn_ctx->endian_convert
        );
    if(SDB_OK != rc)
    {
        goto error;
    }
done:
    return rc;
error:
    goto done;
}


INT32 qc::build_cr_next_command(
    qc::ConnectionContext* conn_ctx,
    SINT64 context_id,
    INT64 total_read
)
{
    INT32 rc = SDB_OK;
//    bson::BSONObj local_obj;
//    MsgOpReply* reply = NULL;
    
    rc = clientBuildGetMoreMsg(
        &(conn_ctx->send_buff),
        &(conn_ctx->send_buff_size),
        -1,
        context_id,
        0,
        conn_ctx->endian_convert);
    if(SDB_OK != rc)
    {
        goto error;
    }

done:
    return rc;
error:
    goto done;
}



INT32 qc::_build_command(
    qc::ConnectionContext* conn_ctx,
    const CHAR* cmd,
    const bson::BSONObj* arg1,
    const bson::BSONObj* arg2,
    const bson::BSONObj* arg3,
    const bson::BSONObj* arg4
)
{
    INT32 rc = SDB_OK;
    SINT64 context_id = 0;

    rc = clientBuildQueryMsgCpp(
        &(conn_ctx->send_buff),
        &(conn_ctx->send_buff_size),
        cmd,
        0, 0, -1, -1, // copy from clientcpp.cpp
        arg1?arg1->objdata():NULL,
        arg2?arg2->objdata():NULL,
        arg3?arg3->objdata():NULL,
        arg4?arg4->objdata():NULL,
        conn_ctx->endian_convert);
    if(SDB_OK != rc)
    {
        goto error;
    }

done:
    return rc;
error:
    goto done;
}


INT32 qc::build_bulk_insert_command(
    qc::ConnectionContext* conn_ctx,
    const CHAR* cs_name,
    const CHAR* c_name,
    SINT32 flag,
    std::vector<std::string>& records
//    const CHAR* bytes_record//,
//    bson::OID* id
)
{
    INT32 rc = SDB_OK;
    SINT32 num = records.size();
    if(!cs_name \
        || !c_name \
        || ossStrlen(c_name) == 0 \
        || ossStrlen(cs_name) == 0 \
        || num == 0 \
    )
    {
        rc = SDB_INVALIDARG;
        return rc;
    }
    std::string c_fullname = std::string(cs_name) + "." + std::string(c_name);

    for(int i = 0; i < num; ++i)
    {
        bson::BSONObj tmp;
        bson::BSONObj* obj = new bson::BSONObj(records[i].data());
        if(!obj)
        {
            rc = SDB_OOM;
            goto error;
        }
        rc = _append_oid(*obj, tmp);
        if(rc)
        {
            delete obj;
            goto error;
        }
        if(0 == i)
        {
            rc = clientBuildInsertMsgCpp(
                &(conn_ctx->send_buff),
                &(conn_ctx->send_buff_size),
                c_fullname.c_str(),
                flag,
                0,
                tmp.objdata(),
                conn_ctx->endian_convert);
        }
        else
        {
            rc = clientAppendInsertMsgCpp(
                &(conn_ctx->send_buff),
                &(conn_ctx->send_buff_size),
                tmp.objdata(),
                conn_ctx->endian_convert);
        }
        if(rc)
        {
            delete obj;
            goto error;
        }
        delete obj;
    }
done:
    return rc;
error:
    goto done;
}
